Skip to content

Z projection 5/6: the run side (part of #176) - #217

Merged
guijacquemet merged 11 commits into
mainfrom
claude/z-projection-run
Nov 8, 2026
Merged

guijacquemet merged 11 commits into
mainfrom
claude/z-projection-run

Conversation

@guijacquemet

@guijacquemet guijacquemet commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Base is main; origin/main (through #228) is merged in (CHANGELOG and docs/contract.md conflicts resolved by keeping both sides), so the diff shows this PR only.

What changed

Fifth PR of the plan for #176 (design: MorphoFeature docs/restart/designs/pp176-z-projection-design-2026-11-07.md, PR 5 and the owner answers: every image goes through the method; 2-D masks are paired with a recorded warning; 3-D masks stop the projection; one optional --z-range).

  • Manifest projection key (optional, left out for an unprojected run, so that manifest is byte for byte as before): method, z_range requested, conversion_record {file, sha256} (the source record, same SHA-256 as conversion_record_sha256), source_root, per analysed image source_relative_path, source_content_sha256, source_z_size, z_range (slices used), source_dtype, output_dtype, dtype_rule, rounding, significant_bits_dropped, plus masks_paired and warnings. Read strictly (exact keys, valid method, SHA-256 shape, range inside the size, warnings consistent with masks); manifest_version is not changed.
  • RunInputs.projection: a RunProjection (method, z_range, conversion_record, source_root, images, image(relative_path), masks_paired, warnings), or None for an unprojected run.
  • Strict reading of the record. RunInputs.open refuses (RunInputsError): a manifest whose projection is not exactly what conversion record v3 gives for the recorded images and masks; projected copies recorded with no projection; a projection with no conversion record; a record changed or gone. Nothing is read as a plain image.
  • masks_on_projected_images warning (names the method, lists the masks) raised in Preflight/check/run, recorded in the manifest, shown in the report. A 3-D mask next to a projected copy still stops the run as mask_planes_mismatch.
  • Report: max projections of slices 1:4 (2 images) of <source> in the text and conversion.projection in discovery_report.json (key only for projected copies).
  • New modules projection_run.py, run_inputs_projection.py and projection_marker.py (layers declared). Docs: contract §9 (manifest row), §9.1, §17, user guide, CHANGELOG.

Owner decisions (2026-11-08)

  1. Projected copies whose conversion record was deleted: REFUSED. check and run read each analysed TIFF's OME Image/Description (the first page's description tag; header only). convert --project writes projected copies with a description beginning "MorphoBricks Z projection:" (projection_record.MARKER, the same constant the writer uses). An image with that marker, in a tree with no conversion record or whose record does not list it as projected, is an error projection_record_missing: it names the images and says to rerun convert --project or restore the conversion record. Plain images without the marker are unaffected; nothing is guessed (a non-TIFF, or a header that cannot be read, is not marked).
  2. Manifest version: kept (no change).
  3. RunInputs.projection run-level property: kept.
  4. masks_on_projected_images: stays warning-only.

Cost of the new check. One TIFF header read per analysed image that the record does not already list as projected (none for a normal projected tree; one per image for a plain tree). It is the page description tag only: no pixel read is added (a test patches TiffPage.asarray to fail and the marker check still passes). An unprojected run is unchanged: tests/test_parity_v0_4_0.py passes.

Regression evidence

  • tests/test_run_projection.py (11 tests, unchanged): regression for the manifest/RunInputs projection.
  • tests/test_projection_marker.py (4 tests). The regression is test_projected_copies_without_their_record_are_refused: a projected tree with its record deleted. On the previous branch tip behaviour (check removed) the file gives 1 failed, 3 passed (the tree is accepted); with the check: 4 passed. The other tests: a record that lists the image as copied does not vouch for it; a plain OME-TIFF without the marker is unaffected (no projection_record_missing, no conversion review); the check reads no pixels.

Checks

python tools/check.py: Ruff, format and code rules clean (files at most 600 lines; manifest.py is 593; no parameter or line baseline grew: to_dict's labels were compacted to stay inside its line baseline); 4634 passed, 4 skipped, 754 warnings. The 4 skips are the pre-existing ones (case-insensitive file system x2, x1 more, file modes x1). The 754 warnings are the existing "No experiment description" UserWarning. Before the main merge and this change the same suite gave 4401 passed (main's new tests account for the rest, plus the 4 new here).

Not in this series yet

PR 6 (notebook card) and PR 7 (homogeneity key). CellPatcher and MorphoFeature are not edited; they can read RunInputs.projection once this lands.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
The manifest records a projection summary for projected copies, RunInputs.projection
exposes it, the run warns masks_on_projected_images, and RunInputs refuses a manifest that
disagrees with the conversion record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
Base automatically changed from claude/z-projection-cli to main November 8, 2026 05:33
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
The Windows CI job failed: the record restored with write_text gained CRLF
line endings, so its SHA-256 no longer matched the run's and RunInputs
refused it. Restore the original bytes instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
@guijacquemet
guijacquemet merged commit d7a54a6 into main Nov 8, 2026
7 checks passed
@guijacquemet
guijacquemet deleted the claude/z-projection-run branch November 8, 2026 09:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants